// 生成虚拟dom和真是dom的源码实现

/**
    封装一个对象迭代的方法
        + 基于传统的for/in 循环，会存在一些弊端 （性能较差；即可以迭代私有的，也可以迭代公有的(无论公有里面有没有他都会去找；直到找到Object
        只能迭代 可枚举的、非Symbol类型的"属性"
        枚举：可以被遍历的一些数据  
        + 解决思路：获取对象所有的私有属性 私有的、不论是否可枚举、不论类型
        Object.getOwnPropertyNames(对象)  ---》获取对象非symbol类型的私有属性，无关是否可以枚举
        Object.getOwnPropertySymbols(对象)  ---》获取Symbol类型的私有属性
        获取所有的私有属性
        let key =  Object.getOwnPropertyNames(对象).concat( Object.getOwnPropertySymbols(对象))
        可以基于Es6中的Reflect.ownKeys(对象) 弊端 ：不兼容IE
    可以用以上的方法来代替for in 循环，专门用来迭代对象中的每一项
 */

//  封装的一个循环对象的函数
const each = function(obj,callback){
    if(obj === null || typeof obj !== "object") throw new TypeError('object is not a object');
    if(typeof callback !== 'function') throw new TypeError('object is not a function');
    // 获取所有的私有属性
    let keys = Reflect.ownKeys(obj)
    keys.forEach(key =>{
        let value = obj[key];
        // 每一次迭代，都把回调函数执行
        callback(value,key)
    })
};


// 该文件为createElement创建虚拟dom的简单过程
// 创建虚拟DOM
export default function createElement(ele,props,...children){
    let virtualDOM ={
        $$typeof:Symbol('react.element'),
        key:null,
        ref:null,
        type:null,
        props:{}
    }
    virtualDOM.type = ele
    // 只有一条的时候，children不是一个数组
    if(!Array.isArray(children)){
        children = [children]
    }
    let len = children.length
    if(props !=null){
        virtualDOM.props = {...props}
    }
    if(len === 1) virtualDOM.props.children = children[0]
    if(len>1) virtualDOM.props.children = children
    return virtualDOM
}

// 虚拟DOM转换成真实的DOM

export function render(virtualDOM,container){
    let {type,props} = virtualDOM
    if(typeof type === 'string'){ //存储的是标签名，我们需要动态的创建标签
        let ele = document.createElement(type)
        // 为标签设置相关的属性 和 子节点
        // 为元素设置属性有两种方式
            // 一、元素.属性 = 属性
            //     原理:对于内置属性，是设置在元素标签上的，对于自定义属性，是给对象的堆内存空间中新增成员【不会设置在标签上】
            //     获取：元素.属性
            //     删除: delete 元素.属性
            // 二、元素.setAttribute(属性，属性值)
            //     原理：直接在元素的标签上写属性
            //     获取：getAttribute()
            //     删除：removeAttribute()

        each(props,(value,key)=>{
            // className 处理情况
            if(key==='className'){
                ele.className = value
                return
            }
            // style 处理，value存储的是样式对象
            if(key === 'style'){    
                each(value,(val,attr)=>{
                    ele.style[attr] = val
                })
                return 
            }   
            // 子节点的处理
            if(key === 'children'){
                let children = value
                if(!Array.isArray(children)) children = [children] // 当他为字符串的时候，也将他变成一个数组
                children.forEach(child =>{
                    // 子节点是文本或则是数值类型f
                    if(/^(string|number)$/.test( typeof child)){
                        ele.appendChild(document.createTextNode(child))
                        return
                    }
                    // 子节点是一个virtualDOM 递归处理
                    render(child,ele)
                })
                return 
            }
            // 正常情况
            ele.setAttribute(key,value)
        })


        // 把新增的标签添加到指定容器中
        container.appendChild(ele)
    }
}